﻿using MongoDB.Driver;
using MongoService.Model;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MongoService.Common
{
    internal class MongoDBHelper<U>
    {
        #region 连接字符串
        public static string DefaultConnectionString
        {
            get { return ConfigurationManager.AppSettings["MongoDbConnectionString"]; }
        }
        public static string DefaultDataBaseName
        {
            get { return ConfigurationManager.AppSettings["ActivityMongoDBName"]; }
        }

        #endregion

        #region 获取IMongoDatabase对象

        /// <summary>
        /// 获取IMongoDatabase对象
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="databaseName"></param>
        /// <returns></returns>
        private static IMongoDatabase GetMongoDatabase(string connectionString, string databaseName)
        {
            if (string.IsNullOrEmpty(connectionString)) connectionString = DefaultConnectionString;
            if (string.IsNullOrEmpty(databaseName)) databaseName = DefaultDataBaseName;

            var client = new MongoClient(connectionString);
            return client.GetDatabase(databaseName);
        }

        /// <summary>
        /// 获取IMongoDatabase对象
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private static IMongoDatabase GetMongoDatabase(MongoUrl url)
        {
            if (url == null) url = new MongoUrl(DefaultConnectionString);
            var client = new MongoClient(url);
            return client.GetDatabase(DefaultDataBaseName);
        }

        #endregion

        /// <summary>
        /// 获取表对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collectionName"></param>
        /// <param name="connectionString"></param>
        /// <param name="databaseName"></param>
        /// <returns></returns>
        public static IMongoCollection<T> GetCollection<T>(string connectionString, string databaseName) where T : IMongoEntity<U>
        {
            var collectionName = GetCollectionName<T>();
            return GetMongoDatabase(connectionString, databaseName).GetCollection<T>(collectionName);
        }

        /// <summary>
        /// 获取表对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collectionName"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static IMongoCollection<T> GetCollection<T>(MongoUrl url) where T : IMongoEntity<U>
        {
            var collectionName = GetCollectionName<T>();
            return GetMongoDatabase(url).GetCollection<T>(collectionName);
        }

        #region 获取CollectionName

        /// <summary>
        /// Determines the collectionname for T and assures it is not empty
        /// </summary>
        /// <typeparam name="T">The type to determine the collectionname for.</typeparam>
        /// <returns>Returns the collectionname for T.</returns>
        private static string GetCollectionName<T>() where T : IMongoEntity<U>
        {
            string collectionName;
            if (typeof(T).BaseType.Equals(typeof(object)))
            {
                collectionName = GetCollectioNameFromInterface<T>();
            }
            else
            {
                collectionName = GetCollectionNameFromType(typeof(T));
            }

            if (string.IsNullOrEmpty(collectionName))
            {
                throw new ArgumentException("Collection name cannot be empty for this entity");
            }
            return collectionName;
        }

        /// <summary>
        /// Determines the collectionname from the specified type.
        /// </summary>
        /// <typeparam name="T">The type to get the collectionname from.</typeparam>
        /// <returns>Returns the collectionname from the specified type.</returns>
        private static string GetCollectioNameFromInterface<T>()
        {
            string collectionname;

            // Check to see if the object (inherited from Entity) has a CollectionName attribute
            var att = Attribute.GetCustomAttribute(typeof(T), typeof(CollectionNameAttribute));
            if (att != null)
            {
                // It does! Return the value specified by the CollectionName attribute
                collectionname = ((CollectionNameAttribute)att).CollectionName;
            }
            else
            {
                collectionname = typeof(T).Name;
            }

            return collectionname;
        }

        /// <summary>
        /// Determines the collectionname from the specified type.
        /// </summary>
        /// <param name="entitytype">The type of the entity to get the collectionname from.</param>
        /// <returns>Returns the collectionname from the specified type.</returns>
        private static string GetCollectionNameFromType(Type entitytype)
        {
            string collectionname;

            // Check to see if the object (inherited from Entity) has a CollectionName attribute
            var att = Attribute.GetCustomAttribute(entitytype, typeof(CollectionNameAttribute));
            if (att != null)
            {
                // It does! Return the value specified by the CollectionName attribute
                collectionname = ((CollectionNameAttribute)att).CollectionName;
            }
            else
            {
                if (typeof(MongoEntityBase).IsAssignableFrom(entitytype))
                {
                    // No attribute found, get the basetype
                    while (!(entitytype.BaseType == typeof(MongoEntityBase)))
                    {
                        entitytype = entitytype.BaseType;
                    }
                }
                collectionname = entitytype.Name;
            }

            return collectionname;
        }
        #endregion

    }
}
